﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Smart.Hotfix
{
    public interface IBaseFrame
    {
        void Create(Model.FrameData frameData);
        void Visible();
        void Destroy();
        void Close();
        long Guid { get; }
        int Layer { get; }
    }

    public static class FrameLayerConst
    {
        public const int Bottom = 0;
        public const int Middle = 1;
        public const int High = 2;
        public const int Top = 3;
        public const int Tips = 4;
        public const int Error = 5;
    }

    public abstract class UIBaseFrame : IBaseFrame
    {
        protected object userData;
        protected Model.UI.ResourceComponent resourceComponent;
        protected Model.UI.FrameComponent frameComponent;
        static long ms_guid = 0x34dd5eL;
        public long Guid
        {
            get;private set;
        }
        public virtual int Layer => FrameLayerConst.Bottom;
        protected bool Disposed
        {
            get
            {
                return null == this.frameComponent || this.frameComponent.Disposed;
            }
        }

        public void Create(Model.FrameData frameData)
        {
            if(null != frameData && null != frameData.frameComponent && !frameData.frameComponent.Disposed)
            {
                Guid = ++ms_guid;
                Entry.Instance.FrameComponent.AddFrame(this);
                this.userData = frameData.argv;
                this.resourceComponent = frameData.resourceComponent;
                this.frameComponent = frameData.frameComponent;
                OnCreate();
            }
        }

        public void Visible()
        {
            if(!this.Disposed)
            {
                OnVisible();
            }
        }

        public void Destroy()
        {
            if (this.Disposed)
                return;
            OnDestroy();
            this.userData = null;
            this.resourceComponent = null;
            this.frameComponent = null;
            Entry.Instance.FrameComponent.RemoveFrame(this.Guid);
            this.Guid = 0;
        }

        public void Close()
        {
            if (this.Disposed)
                return;
            this.frameComponent.Dispose();
        }

        protected abstract void OnCreate();
        protected abstract void OnDestroy();
        protected abstract void OnVisible();
    }

    [ObjectSystem]
    public class FrameComponentAwakeSystem : AwakeSystem<FrameComponent>
    {
        public override void Awake(FrameComponent self)
        {
            self.Awake();
        }
    }

    public class FrameArguments<A>
    {
        public A a;
    }

    public class FrameArguments<A,B>
    {
        public A a;
        public B b;
    }

    public class FrameArguments<A,B,C>
    {
        public A a;
        public B b;
        public C c;
    }

    public class FrameArguments<A,B,C,D>
    {
        public A a;
        public B b;
        public C c;
        public D d;
    }

    public class FrameComponent : Component
    {
        Dictionary<long, IBaseFrame> mFrames = new Dictionary<long, IBaseFrame>(128);
        public void Awake()
        {
            mFrames.Clear();
        }

        long OpenFrame<T>(object argv) where T : IBaseFrame,new()
        {
            var frame = new T();
            var frameData = Model.Entry.Instance.ObjectPool.GetData<Model.FrameData>();
            frameData.bundleName = typeof(T).Name.ToLower();
            frameData.assetName = typeof(T).Name.ToLower();
            frameData.onFrameLoaded = frame.Create;
            frameData.onFrameVisible = frame.Visible;
            frameData.onFrameDestroy = frame.Destroy;
            frameData.argv = argv;
            frameData.layer = frame.Layer;
            Model.Entry.Instance.Scene.AddComponent<Model.UI.FrameComponent, Model.FrameData>(frameData);
            return frame.Guid;
        }

        public long OpenFrame<T,A>(A a) where T : IBaseFrame, new()
        {
            return OpenFrame<T>(new FrameArguments<A>
            {
                a = a,
            });
        }

        public long OpenFrame<T, A,B>(A a,B b) where T : IBaseFrame, new()
        {
            return OpenFrame<T>(new FrameArguments<A,B>
            {
                a = a,
                b = b,
            });
        }

        public long OpenFrame<T, A, B,C>(A a, B b,C c) where T : IBaseFrame, new()
        {
            return OpenFrame<T>(new FrameArguments<A, B,C>
            {
                a = a,
                b = b,
                c = c,
            });
        }

        public long OpenFrame<T, A, B, C,D>(A a, B b, C c,D d) where T : IBaseFrame, new()
        {
            return OpenFrame<T>(new FrameArguments<A, B, C,D>
            {
                a = a,
                b = b,
                c = c,
                d = d,
            });
        }

        public void CloseFrame(long guid)
        {
            if(mFrames.ContainsKey(guid))
            {
                var frame = mFrames[guid];
                mFrames.Remove(guid);
                frame.Close();
            }
        }

        public void AddFrame(IBaseFrame frame)
        {
            if(null != frame)
            {
                mFrames.Add(frame.Guid, frame);
            }
        }

        public void RemoveFrame(long guid)
        {
            mFrames.Remove(guid);
        }

        public override void Dispose()
        {
            if (Disposed)
            {
                return;
            }

            while(mFrames.Count > 0)
            {
                var it = mFrames.GetEnumerator();
                it.MoveNext();
                it.Current.Value.Destroy();
            }

            base.Dispose();
        }
    }
}